package viper.receive;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

import viper.main.StateManager;
import viper.main.ViperHeader;
import viper.send.UDPSender;

public class UDPReceiverWorker implements Runnable {
	
	public DatagramPacket packet;
	private byte[] bytes;
	private byte[] temp;

	public UDPReceiverWorker(DatagramPacket packet)
	{
		this.packet = packet;
	}
	
	@Override
	public void run() {
		//parse packet
		System.out.println("UDPWorker got packet");
		bytes = packet.getData();
		
		switch(bytes[0]) {
			case ViperHeader.RELAY:
				handleRelayPacket();
				break;
			case ViperHeader.RECEIVE: 
				handleReceivePacket();
				break;
			case ViperHeader.CALL_SETUP: 
				String sourceAddrIP = packet.getAddress().getHostAddress();
				handleCallSetupPacket(sourceAddrIP);
				break;
			default: 
				System.out.println("ignore"); 
				break;
		} 
	}
	
	private void handleCallSetupPacket(String sourceAddrIP)
	{
		System.out.println("Call Setup");
		switch(bytes[1]) {
			case ViperHeader.SETUP_MAKE_CALL: //somebody wants to talk
				byte[] payload = ViperHeader.getPayloadFromSetupPacket(bytes);
				String[] relays = null; //TODO: populate
				byte typeCode = bytes[2];
				StateManager.INSTANCE.gotCall(sourceAddrIP, typeCode, relays);
				break;
			case ViperHeader.SETUP_ACCEPT_CALL:
				//person accept your call
				StateManager.INSTANCE.dialAccepted();
				break;
			case ViperHeader.SETUP_ERROR:
				//handle error
				break;
			case ViperHeader.SETUP_END_CALL:
				//call ended
				StateManager.INSTANCE.callEnded();
				break;
			default: 
				System.out.println("ignored call setup packet"); 
				break; 
		}
	}
	
	/* At a relay, upon receiving a packet from the Sender, the relay changes the VIPER header
	of the packet and forwards it to the sender.
	*/ 
	private void handleRelayPacket() {
		
		System.out.println("Relay");
		//temp is a temporary byte[] to store the payload for the packet to be sent to the RX
		temp = new byte[bytes.length - 5];
		temp[0] = ViperHeader.RECEIVE;
		System.arraycopy(bytes, 5, temp, 1, bytes.length - 5);
		packet.setData(temp);
		byte[] ipByte = new byte[4];
		System.arraycopy(bytes, 1, ipByte, 0, 4);
		InetAddress rxAddr = null;
		
		try {
			rxAddr = InetAddress.getByAddress(ipByte);
			packet.setAddress(rxAddr);
			StateManager.INSTANCE.getDatagramSocket().send(packet);
			
		} catch (UnknownHostException e1) {
			e1.printStackTrace();
		}catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/* At the receiver, when a receive packet is received, they are sorted in order in 
	 * RCV_BUFFER if they are within a valid sliding window range.
	 */
	private void handleReceivePacket()
	{
		System.out.println("Receive"); 
		
		byte[] buff = null;
		int seqNum, pktType;
		byte[] data = null;
		
		/*Format of packet received at receiver side:
		 * <seq num (1 byte)> <pkt type (1 byte)> <payload> */
				
		//Check packet seq num
		//byte[] buff is the temporary buffer to store the payload of UDP packet
		buff = packet.getData();
		seqNum = buff[0];
		
		//If packet received is within sliding window range, 
		//then store packet data in rcvBuffer
		if((seqNum >= CallReceiver.WINDOW_INDEX) && (seqNum < (CallReceiver.WINDOW_INDEX + CallReceiver.WINDOW_SIZE))){
			pktType = buff[1];
			
			//Strip VIPER header off UDP packet payload and store voice data in byte[] data
			System.arraycopy(buff,2,data,0,buff.length-2);
			
			switch (pktType){
				case 0: CallReceiver.RCV_BUFFER[seqNum][0] = data; break;
				case 1: CallReceiver.RCV_BUFFER[seqNum][1] = data; break;
				case 2: CallReceiver.RCV_BUFFER[seqNum][2] = data; break;
				default: break;	
			}
		}
	}
	
	
	//////////////////////////////////////////////////////////////////////////////////
	//De-diversification logic

	/*
	// The code below tests de-diversification for (2,3,2) scheme with all type 2 packets lost.
	byte[] buff = new byte[maxPacketSize]; //Storage buffer for incoming packets
	byte[] buff1 = null; //Storage for VHeader = 1 type packets
	byte[] buff3; //Storage for VHeader = 3 type packets
	byte[] buffrecovered=null; //also VHeader = 2
	*/
	
	/*
	// If packet is type 1, save payload into buff1 and update the stream since this packet wasn't encoded.
	///* UNCOMMENT ME
		if(buff[0]==1){ //Examine VIPER header...
			buff1= new byte[buff.length-1];
			System.arraycopy(buff,1,buff1,0,buff.length-1);
			if(p!=null){
				//System.out.println("Packet Received!!");
				rxls.update(buff1);

			}
		}
		// If packet is type 3, then decode using type 1 packet and update stream with recovered type 2 packet.
		else if(buff[0]==3){
			buff3= new byte[buff.length-1];
			buffrecovered= new byte[buff.length-1];
			System.arraycopy(buff,1,buff3,0,buff.length-1);
			//Perform the decoding with XOR...
			 for (int i =0;i<buff3.length;i++){
			     buffrecovered[i]=(byte)(buff1[i]^buff3[i]);
			     System.out.print(buffrecovered[i]);
			    }
			 if(p!=null){
					//System.out.println("Packet Received!!");
					rxls.update(buffrecovered);
				}
		}				
	*/
	
	
	
	
}
